#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>

FILE *f1, *f2, *fs;
int m = -1;			/* define el max de errores a mostrar */

int compareChars () {
  int col = 0;
  int line = 0;
  int cols_f1 = 1;
  int cols_f2 = 1;
  int first_error = 1;
  int cant_errors = 0;
	int cant_errors_total = 0;
  int is_eof = 0;
  char msg[500] = "\0";
  char aux[15] = "\0";
  int c1, c2;

  c1 = fgetc (f1);
  c2 = fgetc (f2);
  while (!is_eof) {

    /* se terminaron ambos archivos */
    if ((c1 == EOF) && (c2 == EOF)) {
      is_eof = 1;
      continue;
    }

    /* los caracteres son distintos */
    if (c1 != c2) {
      cant_errors++;
			cant_errors_total++;
      if (((m != -1) && (cant_errors_total <= m)) || (m == -1)) {	/* almaceno solo la cantidad de errores pedida o todo */
				if (first_error) {
					sprintf (msg, "line: %d - Cols: %d", line, col);
					first_error = 0;
				} else {
					sprintf (aux, ", %d", col);
					sprintf (msg,"%s%s", msg, aux);
				}
      }
    }

    /* termino una linea de f1 o finaliza y el otro no */
    if (((c1 == 10) && (c2 != 10)) || ((c1 == EOF) && (c2 != EOF))) {
      c2 = fgetc (f2);
      cols_f2++;
      col++;
    } else if (((c1 != 10) && (c2 == 10)) || ((c1 != EOF) && (c2 == EOF))) {	/* caso inverso */
      c1 = fgetc (f1);
      cols_f1++;
      col++;
    } else if ((c1 == 10) && (c2 == 10)) {	/* linea nueva */
      cols_f1--;
      cols_f2--;		/* elimino los saltos de linea del conteo de caracteres */
      if ((cant_errors) && ((cant_errors == cols_f1) || (cant_errors == cols_f2))) {	/* diff en toda la linea */
				if ((m > cant_errors_total) || (m == -1))
					fprintf (fs, "line: %d - Cols: all line\n", line);
				else 
					if (cant_errors)
						fprintf (fs, "%s\n", msg);		
      } else {			/* imprimo las diferencias si hay */
					if (cant_errors)
						fprintf (fs, "%s\n", msg);
			}
			if ((cant_errors_total >= m)&&(m!=-1)) /* si ya encontro m errores, corta la lectura de los archivos */
						break;
			line++;
			col = 0;
			cols_f1 = 1;
			cols_f2 = 1;
			c1 = fgetc (f1);
			c2 = fgetc (f2);
			cant_errors = 0;
			strcpy (msg, "");
			first_error = 1;
    } else {			/* no pasa nada, leo un caracter mas de cada archivo */
      c1 = fgetc (f1);
      c2 = fgetc (f2);
      cols_f1++;
      cols_f2++;
      col++;
    }
  }
  return 0;
}

/**
* Abre los archivos a ser procesados mostrando mensaje si ocurre
* algun error. Toma los primeros caracteres e invoca las comparaciones
**/

int process (char *file1, char *file2, char *output) {

  f1 = fopen (file1, "r");
  if (!f1) {
    fprintf (stderr, "Error al abrir el archivo %s\n", file1);
    return -1;
  }

  f2 = fopen (file2, "r");
  if (!f2) {
    fprintf (stderr, "Error al abrir el archivo %s\n", file2);
    fclose (f1);
    return -1;
  }

  if (!output)
    fs = stdout;
  else {
    fs = fopen (output, "w");
    if (!fs) {
      fprintf (stderr, "Error al abrir el archivo %s\n", output);
      fclose (f1);
      fclose (f2);
      return -1;
    }
  }

  compareChars ();

  fclose (f1);
  fclose (f2);
  fclose (fs);
  return 0;
}


void imprime_version () {
  printf ("diff - version: 0.2\n");
}

void imprime_uso () {
  printf ("   Usage:\n\n"
	  "   diff [options] from-file to-file\n"
	  "   diff - find differences between two files\n\n"
	  "   Options:\n"
	  "   		-v, --version      Print version and quit.\n"
	  "   		-h, --help         Print this information and quit.\n"
	  "   		-o, --output       Specifies the output file.\n"
	  "   		-m, --max          Shows the first m differences.\n");
}

int main (int argc, char *argv[]) {
  int c;
  char *output_file = NULL;
	int solo_texto=0;

  while (1) {
    static struct option long_options[] = {
      {"version", no_argument, 0, 'v'},
      {"help", no_argument, 0, 'h'},
      {"output", required_argument, 0, 'o'},
      {"max", required_argument, 0, 'm'},
      {0, 0, 0, 0}
    };
    int option_index = 0;
    c = getopt_long (argc, argv, "m:o:vh", long_options, &option_index);

    if (c == -1)
      break;

    switch (c) {
    case 'v':
      imprime_version ();
			solo_texto=1;
      break;

    case 'h':
      imprime_uso ();
			solo_texto=1;
      break;

    case 'o':
      output_file = optarg;
      break;

    case 'm':
      m = atoi (optarg);
      break;

    case '?':
      fprintf (stderr, "Error en los parámetros\n");
      return -1;
      break;

    default:
      abort ();
    }
  }

  if (optind < argc) {
    	process (argv[optind], argv[optind + 1], output_file);
  } else {
		if (!solo_texto)
			fprintf (stderr, "Debe específicar los archivos de entrada\n");
	}
  return 0;
}
